﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Text;

namespace Xceed.Silverlight.Data
{
  public abstract class FilterExpressionFormatter : FilterExpressionVisitor
  {
    #region Constructor

    protected FilterExpressionFormatter()
      : base()
    {
    }

    protected FilterExpressionFormatter( FilterExpressionBase expression )
      : base( expression )
    {
    }

    #endregion

    #region PartialResult Protected Property

    protected StringBuilder PartialResult
    {
      get
      {
        return m_partialResult;
      }
    }

    private StringBuilder m_partialResult = new StringBuilder();

    #endregion

    protected override void Initialize()
    {
      m_partialResult.Clear();

      base.Initialize();
    }

    protected virtual string GetFormat()
    {
      // This is the format that will be use for every expression of 
      // type FilterExpressionBase.
      return "({0})";
    }

    protected virtual string GetFormat( FilterExpression expression )
    {
      // {0}: Property name
      // {1}: Filter operator
      // {2}: Value
      switch( expression.FilterOperator )
      {
        case FilterOperator.StartsWith:
        case FilterOperator.EndsWith:
        case FilterOperator.Contains:
        case FilterOperator.ContainedIn:
          return "{0}.{1}( {2} )";

        default:
          break;
      }

      return "{0} {1} {2}";
    }

    protected virtual string GetFormat( AndFilterExpression expression )
    {
      return FilterExpressionFormatter.GetFormat( expression, "And" );
    }

    protected virtual string GetFormat( OrFilterExpression expression )
    {
      return FilterExpressionFormatter.GetFormat( expression, "Or" );
    }

    protected virtual string GetFormat( NotFilterExpression expression )
    {
      return "Not {0}";
    }

    protected virtual string GetMemberName( FilterExpression expression )
    {
      return string.Format( "{{{0}}}", expression.MemberName );
    }

    protected virtual string GetFilterOperator( FilterExpression expression )
    {
      switch( expression.FilterOperator )
      {
        case FilterOperator.Equal:
          return "==";

        case FilterOperator.NotEqual:
          return "!=";

        case FilterOperator.GreaterThan:
          return ">";

        case FilterOperator.GreaterThanOrEqual:
          return ">=";

        case FilterOperator.LessThan:
          return "<";

        case FilterOperator.LessThanOrEqual:
          return "<=";

        case FilterOperator.StartsWith:
          return "StartsWith";

        case FilterOperator.EndsWith:
          return "EndsWith";

        case FilterOperator.Contains:
          return "Contains";

        case FilterOperator.ContainedIn:
          return "ContainedIn";

        default:
          return "?";
      }
    }

    protected virtual string GetValue( FilterExpression expression )
    {
      object value = expression.Value;

      if( value == null )
        return this.GetNullValue();

      if( value is string )
        return string.Format( "'{0}'", value );

      if( value is DateTime )
        return string.Format( "#{0}#", value );

      return value.ToString();
    }

    protected virtual string GetNullValue()
    {
      return "#null#";
    }

    protected internal override void Visit( FilterExpression expression )
    {
      // Retrieve the string representation of each part of the filter expression.
      string memberName = this.GetMemberName( expression );
      string filterOperator = this.GetFilterOperator( expression );
      string value = this.GetValue( expression );

      string format = this.GetFormat( expression );
      string result = string.Format( format, memberName, filterOperator, value );

      this.AppendExpressionResult( result );
    }

    protected internal override void Visit( AndFilterExpression expression )
    {
      string format = this.GetFormat( expression );

      this.Visit( expression, format );
    }

    protected internal override void Visit( OrFilterExpression expression )
    {
      string format = this.GetFormat( expression );

      this.Visit( expression, format );
    }

    protected internal override void Visit( NotFilterExpression expression )
    {
      // Keep a copy of the partial result.
      string previousResult = this.FlushPartialResult();

      // Generate the content of the child expression.
      this.Visit( expression.ChildExpression );

      // Retrieve the content of the child expression.
      string childResult = this.FlushPartialResult();

      // Restore the original partial result.
      StringBuilder sb = this.PartialResult;
      sb.Append( previousResult );

      // Append the current expression to the partial result.
      string format = this.GetFormat( expression );
      string result = string.Format( format, childResult );

      this.AppendExpressionResult( result );
    }

    private void Visit( FilterExpressionOperator expression, string format )
    {
      int childCount = expression.ChildExpressions.Count;
      string[] childResults = new string[ childCount ];

      // Keep a copy of the partial result.
      string previousResult = this.FlushPartialResult();

      for( int i = 0; i < childCount; i++ )
      {
        // Generate the content of the child expression.
        this.Visit( expression.ChildExpressions[ i ] );

        // Retrieve the content of the child expression.
        childResults[ i ] = this.FlushPartialResult();
      }

      // Restore the original partial result.
      StringBuilder sb = this.PartialResult;
      sb.Append( previousResult );

      // Append the current expression to the partial result.
      string result = string.Format( format, childResults );

      this.AppendExpressionResult( result );
    }

    private string FlushPartialResult()
    {
      StringBuilder sb = this.PartialResult;
      string partialResult = sb.ToString();
      sb.Clear();

      return partialResult;
    }

    private void AppendExpressionResult( string result )
    {
      string format = this.GetFormat();

      this.PartialResult.AppendFormat( format, result );
    }

    private static string GetFormat( FilterExpressionOperator expression, string operatorGlyph )
    {
      StringBuilder sb = new StringBuilder();
      int count = expression.ChildExpressions.Count;

      for( int i = 0; i < count; i++ )
      {
        if( i > 0 )
        {
          sb.Append( " " ).Append( operatorGlyph ).Append( " " );
        }

        sb.Append( "{" ).Append( i ).Append( "}" );
      }

      return sb.ToString();
    }
  }
}
